package com.bloomingstars.sqm.util;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Class that can be used to gather information about resources that are in the
 * JAR. The main purpose of the class is to provide possibility of loading
 * resources within the JAR and local file system. Includes some methods to
 * generate JAR file list and filter it using regex. See method javadoc for
 * details.
 */
public class JarResourceUtility {

    private static Logger logger = LogManager.getLogger(JarResourceUtility.class);

    /**
     * Gets the JAR file content for given resource. The class loader need to be
     * from the class that is in the JAR. The class need to be in higher package
     * than the resource.
     * 
     * @param classLoader
     * @param resourceName
     * @return
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static Set<String> getJarFileList(ClassLoader classLoader,
        String resourceName) throws IOException {
    Set<JarEntry> jarEntries = getJarContent(classLoader, resourceName);

    if (jarEntries == null)
        return Collections.EMPTY_SET;

    Set<String> names = new HashSet<String>();
    for (JarEntry jarEntry : jarEntries) {
        names.add(jarEntry.getName());
    }
    return names;
    }

    /**
     * Gets the JAR file content for given resource. The class loader need to be
     * from the class that is in the JAR. The class need to be in higher package
     * than the resource.
     * 
     * @param classLoader
     * @param resourceName
     * @return
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static Set<JarEntry> getJarContent(ClassLoader classLoader,
        String resourceName) throws IOException {
    URL resource = classLoader.getResource(resourceName);

    if (resource == null) {
        logger.error("Failed to get URL to resource: " + resourceName);
        return null;
    }

    JarURLConnection urlConnection = (JarURLConnection) resource
        .openConnection();

    JarFile jarFile = urlConnection.getJarFile();
    if (jarFile == null) {
        logger.error("Failed to get JAR file reference for resource "
            + resourceName);
        return null;
    }

    Enumeration<JarEntry> jarEntries = jarFile.entries();
    if (!jarEntries.hasMoreElements()) {
        logger.debug("No jar entries for jar " + jarFile.getName());
        return Collections.EMPTY_SET;
    }

    JarEntry jarEntry = null;
    Set<JarEntry> content = new HashSet<JarEntry>();
    while (jarEntries.hasMoreElements()) {
        jarEntry = (JarEntry) jarEntries.nextElement();
        content.add(jarEntry);
    }

    return content;
    }

    /**
     * Checks does the given resource and is in the JAR file
     * 
     * @param classLoader
     *                class loader for given resource
     * @param resourceName
     *                resource name to look for
     * @return true if resource is in the JAR, otherwise false
     */
    public static boolean isInJarFile(ClassLoader classLoader,
        String resourceName) throws IOException {
    if (classLoader == null || resourceName == null) {
        logger
            .error("isInJarFile(..): Illegal arguments. Null values not allowed");
        return false;
    }
    return (resourceName.contains(".jar!"));
    }

    /**
     * Filters given string list
     * 
     * @param regex
     * @param records
     * @return
     */
    @SuppressWarnings("unchecked")
    public static List<String> filterSet(String regex,
        Collection<String> records) {
    if (regex == null || records == null || records.size() == 0)
        return Collections.EMPTY_LIST;

    List<String> result = new ArrayList<String>();
    Pattern pattern = Pattern.compile(regex);
    for (String record : records) {
        Matcher matcher = pattern.matcher(record);
        if (matcher.find()) {
        result.add(record);
        }
    }
    return result;
    }

    /**
     * Gets jar file for given resource name, if the resource is in the JAR
     * 
     * @param classLoader
     *                class loader for resource
     * @param resourceName
     *                resource name to look for
     * @return jar file for resource name, otherwise null
     * @throws IOException
     *                 in case of I/O problems
     */
    public static JarFile getJarFile(ClassLoader classLoader,
        String resourceName) throws IOException {
    URL resource = classLoader.getResource(resourceName);

    if (resource == null) {
        logger.error("Failed to get URL to resource: " + resourceName);
        return null;
    }

    JarURLConnection urlConnection = (JarURLConnection) resource
        .openConnection();

    return urlConnection.getJarFile();
    }

    /**
     * Gets directory for given resource name. if resource is a file, extracts
     * the directory from it
     * 
     * NOTE: WIll not work within JAR file
     * 
     * @param classLoader
     *                class loader to load the resource
     * @param resourceName
     *                resource name to load
     * @return directory for given resource name, otherwise null
     */
    public static File getResourceDirectory(ClassLoader classLoader,
        String resourceName) {
    if (classLoader == null || resourceName == null) {
        logger
            .error("Unsupported method arguments. null values not allowed");
        return null;
    }

    File resourceFile = getResourceFile(classLoader, resourceName);

    if (resourceFile == null)
        return null;

    if (resourceFile.isDirectory())
        return resourceFile;

    // extract directory
    String directoryName = resourceFile.getParent();
    logger.debug("This the parent = " + directoryName);

    return new File(directoryName);
    }

    /**
     * Gets file for given resource name.
     * 
     * NOTE: WIll not work within JAR file
     * 
     * @param classLoader
     *                class loader to load the resource
     * @param resourceName
     *                resource name to load
     * @return file for given resource name, otherwise null
     */
    public static File getResourceFile(ClassLoader classLoader,
        String resourceName) {
    if (classLoader == null || resourceName == null) {
        logger
            .error("Unsupported method arguments. null values not allowed");
        return null;
    }

    URL resourceURL = classLoader.getResource(resourceName);

    if (resourceURL == null) {
        logger.error("Resource " + resourceName + " not found.");
        return null;
    }

    try {
        return new File(resourceURL.toURI());
    } catch (URISyntaxException e) {
        logger.error("Failed to initialise file for resource name"
            + resourceName);
        e.printStackTrace();
        return null;
    }
    }
}

